﻿using System;
using biz.ritter.javapi.io;
using biz.ritter.javapi.lang;
using biz.ritter.javapi.lang.reflect;
using biz.ritter.javapi.util;
using Nofs.Net.Common.Interfaces.Domain;
using Nofs.Net.Common.Interfaces.Library;
using Nofs.Net.Exceptions;
using Nofs.Net.nofs.metadata.interfaces;

namespace Nofs.Net.Fuse.Impl
{
    /// <summary>
    /// PathTranslator
    /// </summary>
    public class PathTranslator
    {
        private INoFSClassLoader _loader;
        private IDomainObjectContainerManager _manager;
        private IFileObjectFactory _fileObjectFactory;
        private Object _root;
        private static String _separatorChar = "" + File.separatorChar;

        public PathTranslator(INoFSClassLoader loader, IDomainObjectContainerManager manager, IFileObjectFactory factory)
        {
            _root = null;
            _loader = loader;
            _manager = manager;
            _fileObjectFactory = factory;
        }

        //@SuppressWarnings("unchecked")
        public void CreateRootInFileSystem(LogManager logger) //throws Exception 
        {
            Collection<Class> rootClasses = _loader.LoadClassesWithAnnotation(MarkerTypes.RootFolderObject);
            logger.LogInfo("Found " + rootClasses.size() + " root classes");
            if (rootClasses.size() == 0)
            {
                throw new System.Exception("could not find any root classes. cannot create file system.");
            }
            Class firstClass = (Class)rootClasses.toArray()[0];
            IDomainObjectContainerInternal container = (IDomainObjectContainerInternal)_manager.GetContainer(firstClass);
            Object sender = container.NewPersistentInstance();
            container.CreateAndSaveStatObjects(sender);
            logger.LogInfo("Created file system root.");
        }

        //@SuppressWarnings("unchecked")
        private Object GetRootObject() //throws Exception 
        {
            if (_root == null)
            {
                LinkedList<Class> rootClasses = _loader.LoadClassesWithAnnotation(MarkerTypes.RootFolderObject);
                LinkedList<Object> rootObjects = new LinkedList<Object>();
                foreach (Class rootClass in rootClasses)
                {
                    IDomainObjectContainer container = (IDomainObjectContainer)_manager.GetContainer(rootClass);
                    rootObjects.addAll(container.GetAllInstances());
                }
                if (rootObjects.size() != 1)
                {
                    throw new System.Exception("found " + rootObjects.size() + " root file system objects");
                }
                _root = rootObjects.get(0);
            }
            if (_root == null)
            {
                throw new System.Exception("null root!");
            }
            return _root;
        }

        public bool FileSystemHasARoot()
        {
            try
            {
                GetRootObject();
            }
            catch (System.Exception)
            {
            }
            return _root != null;
        }

        private static LinkedList<String> SplitPath(String path)
        {
            LinkedList<String> parts = new LinkedList<String>();
            String pattern = _separatorChar.compareTo("\\") == 0 ? "\\\\" : _separatorChar;
            foreach (String part in path.Split(pattern.ToCharArray()))
            {
                if (part.length() > 0)
                {
                    parts.add(part);
                }
            }
            return parts;
        }

        public IFileObject TranslatePathParent(String path) //throws NoFSDuplicateNameException, NoFSPathInvalidException, Exception 
        {
            String parentPath = PathUtil.GetParentName(path);
            return TranslatePath(parentPath);
        }

        public IFileObject TranslatePath(String path) //throws NoFSDuplicateNameException, NoFSPathInvalidException, Exception 
        {
            Object current = GetRootObject();
            Object methodObj = null;
            LinkedList<String> list = SplitPath(path);
            foreach (String name in list)
            {
                Object newObj;
                if (methodObj == null)
                {
                    newObj = _fileObjectFactory.GetChildWithName(current, name);
                }
                else
                {
                    newObj = _fileObjectFactory.GetChildWithName(methodObj, (Method)current, name);
                }
                if (newObj is Method)
                {
                    methodObj = current;
                }
                else
                {
                    methodObj = null;
                }
                current = newObj;
                if (current == null)
                {
                    break;
                }
            }
            if (current == null)
            {
                return null;
            }
            else
            {
                if (methodObj == null)
                {
                    return _fileObjectFactory.BuildFileObject(PathUtil.GetParentName(path), current);
                }
                else
                {
                    return _fileObjectFactory.BuildFileObject(PathUtil.GetParentName(path), methodObj, (Method)current);
                }
            }
        }

        private static void ThrowIfDuplicateNameFound(String path, LinkedList<String> names)// throws NoFSDuplicateNameException 
        {
            LinkedList<String> seen = new LinkedList<String>();
            foreach (String name1 in names)
            {
                foreach (String name2 in seen)
                {
                    if (name2.compareTo(name1) == 0)
                    {
                        throw new NoFSDuplicateNameException(path, name1);
                    }
                }
                seen.add(name1);
            }
        }

        public LinkedList<IFileObject> GetPathChildren(String path) //throws NoFSDuplicateNameException, NoFSPathIsNotAFolderException, NoFSPathInvalidException, Exception 
        {
            Object current = GetRootObject();
            Object methodObj = null;
            foreach (String name in SplitPath(path))
            {
                Object newObj;
                if (methodObj == null)
                {
                    newObj = _fileObjectFactory.GetChildWithName(current, name);
                }
                else
                {
                    newObj = _fileObjectFactory.GetChildWithName(methodObj, (Method)current, name);
                }
                if (newObj is Method)
                {
                    methodObj = current;
                }
                else
                {
                    methodObj = null;
                }
                current = newObj;
                if (current == null)
                {
                    throw new NoFSPathInvalidException(path);
                }
            }
            if (path.compareTo(_separatorChar) != 0)
            {
                IFileObject file;
                if (methodObj != null)
                {
                    file = _fileObjectFactory.BuildFileObject(PathUtil.GetParentName(path), methodObj, (Method)current);
                }
                else
                {
                    file = _fileObjectFactory.BuildFileObject(PathUtil.GetParentName(path), current);
                }
                if (file.GetGenerationType() != GenerationType.DOMAIN_FOLDER)
                {
                    throw new NoFSPathIsNotAFolderException(path);
                }
            }
            LinkedList<IFileObject> objects = new LinkedList<IFileObject>();
            LinkedList<String> childNames = new LinkedList<string>();
            if (methodObj == null)
            {
                childNames.addAll(_fileObjectFactory.GetChildNames(current));
            }
            else
            {
                childNames.addAll(_fileObjectFactory.GetChildNames(methodObj, (Method)current));
            }
            ThrowIfDuplicateNameFound(path, childNames);
            foreach (String name in childNames)
            {
                Object childObject;
                if (methodObj == null)
                {
                    childObject = _fileObjectFactory.GetChildWithName(current, name);
                }
                else
                {
                    childObject = _fileObjectFactory.GetChildWithName(methodObj, (Method)current, name);
                }
                if (childObject == null)
                {
                    throw new System.Exception("child with name '" + name + "' could not be found");
                }
                IFileObject childFile;
                if (childObject is Method)
                {
                    childFile = _fileObjectFactory.BuildFileObject(PathUtil.GetParentName(path), current, (Method)childObject);
                }
                else
                {
                    childFile = _fileObjectFactory.BuildFileObject(PathUtil.GetParentName(path), childObject);
                }
                objects.add(childFile);
            }
            return objects;
        }
    }

}
